/**
 * --------------------------------------
 * FileName: dual_list.c
 * Content: 
 *      数据结构第3周第一次作业前置内容
 *      一个简单的双向链表
 * 
 * Auther:  杨则瑞
 * Encode:  utf-8 | CRLF
 * --------------------------------------
 */

 
#include<dual_list.h>
#include<debuging.h>



#define ns(lable) duList_##lable

#define try_error(errors) \
do { \
    if(self->_error_handler != NULL)\
    { \
        *self->_error_handler = errors; \
    } \
} while (0)

// Memory
int ns(init) (ns(t) * self, size_t elem_size, ns(error_t)* error_handler)
{
    self->_elem_size = elem_size;
    self->_header = (ns(node_t) *)malloc(sizeof(ns(node_t)));
    if(error_handler != NULL)
    {
        self->_error_handler = error_handler;
    }

    if(self->_header == NULL)
    {
        _debug("Allocation Fault","WARN");

        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    self->_header->next = self->_header;
    self->_header->prev = self->_header;
    return 0;
}


int ns(uninit) (ns(t) * self)
{
    ns(clear) (self);
    free(self->_header);
    return 0;
}

// Modification
int ns(insert) (ns(t) *self, size_t pos, const void * element)
{
    ns(node_t) * p = self->_header;

    for(int i = 0; i < pos; ++ i)
    {        
        p = p->next;

        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);
            return -2;
        }


    }

    ns(node_t) *p_newnode = (ns(node_t) *)malloc(sizeof(ns(node_t)) + self->_elem_size);


    if(p_newnode == NULL)
    {
        _debug("Allocation Fault","WARN");
        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    p->next->prev = p_newnode;
    p_newnode->next = p->next;
    p_newnode->prev = p;
    p->next = p_newnode;
    memcpy(p_newnode->data,element,self->_elem_size);

    return 0;
}

int ns(insert_reverse) (ns(t) * self, size_t pos, const void * element)
{
    
    
    ns(node_t) * p = self->_header;
    
    for(int i = 0; i < pos; ++ i)
    {
        p = p->prev;
        
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);

            return -2;
        }
    }

    ns(node_t) *p_newnode = (ns(node_t) *)malloc(sizeof(ns(node_t)) + self->_elem_size);


    if(p_newnode == NULL)
    {
        _debug("Allocation Fault","WARN");
        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    p->prev->next = p_newnode;
    p_newnode->prev = p->prev;
    p->prev = p_newnode;
    p_newnode->next = p;

    memcpy(p_newnode->data,element,self->_elem_size);

    return 0;

}


int ns(erase)  (ns(t) * self, size_t pos)
{
     
    ns(node_t) * p = self->_header->next;
    
    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        try_error(duList_INDEX_OUT_OF_BOUND);

        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        
        if(p->next == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);

            return -2;
        }

        p = p->next;
        
    }

    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);

    return 0;
}


int ns(erase_reverse)  (ns(t) * self, size_t pos)
{

    ns(node_t) * p = self->_header->prev;
    
    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        try_error(duList_INDEX_OUT_OF_BOUND);

        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        
        if(p->prev == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }

        p = p->prev;
        
    }

    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);

    return 0;
}


int ns(clear)  (ns(t) * self)
{
    ns(node_t) *p = self->_header->next;

    while (p != self->_header)
    {
        p->prev->next = p->next;
        p->next->prev = self->_header;
        free(p);
        p = self->_header->next;
    }

    return 0;
}

// Utility
int ns(is_empty) (const ns(t) * self)
{
    return self->_header->next == self->_header;
}

size_t ns(length)   (const ns(t) * self)
{
    ns(node_t) *p = self->_header->next;
    size_t cnt = 0;
    while (p != self->_header)
    {
        cnt ++;
        p = p->next;
    }
    
    return cnt;
}

int ns(get) (const ns(t) * self, size_t pos, void * val_dest)
{
    ns(node_t) * p = self->_header->next;

    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }
        p = p->next;
    }
    
    memcpy(val_dest, p->data, self->_elem_size);

    return 0;
}


int ns(get_reverse) (const ns(t) * self, size_t pos, void * val_dest)
{
    
    ns(node_t) * p = self->_header->prev;

    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }
        p = p->prev;
    }
    
    memcpy(val_dest, p->data, self->_elem_size);

    return 0;

}

#undef try_error
#undef ns 